//==========================================================================
// Copyright (c) 2000-2006,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <ckernel.h>
#include <mantle.h>
#include <core\objenum.h>

IDeviceDriver    *g_pDebugInfo = NULL;

#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
#define GET_THREAD_USERID(x) ((x->m_uUserStackBase & ~(USER_STACK_ALIGN - 1)) + 1)
#define GET_USERSTACK_BASE(x) (((Thread *)x)->m_uUserStackBase)
#define CRITICALSECTION_FLAG    1
#define MUTEX_FLAG              2

typedef struct OwnerNode : public DLinkNode {
    int nThreadId;
    int event;
} OwnerNode;
#endif //MULTI_RESOURCE_DEADLOCK_DETECT

class CDebugInfo : public CDeviceDriver {
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

public:
	CProcess *m_proc;
private:
	void Show();
	void PrintThreadContext(CProcess *pProcess);
	void PrintProcessContext(CProcess *pProcess);
	void PrintThreadInfo(CProcess *pProcess, Thread *pThread);
	void TraceStack(CProcess *pProcess, Address addr);
	void TraceMemory(Address addr);
	ECode GetModuleBase(CProcess *pProcess, uint32_t retAddr);
	ECode PrintModuleView(CProcess *pProcess);
    Boolean IsValidAddr(CProcess *pProcess, const UInt32 uAddr);
    ECode GetCurrentThreadPhyscialMemorySize(Thread *pCurrentThread, UInt32 *pSize);
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
	//for deadlock detect.
	void DeadLockDetection(Thread *pThread, CProcess *pProcess);
	void OutPutDeadLockInfo(int *pDeadLockInfo, int nDeadLockPos);
	int *GetRequestSynObj(Thread *pThread);
	int *GetSynObjOwner(SyncObject *pSyncObj, CProcess *pProcess);
	Boolean IsCSNodeEvent(SyncObject *pSyncObj, CProcess *pProcess);
#endif //MULTI_RESOURCE_DEADLOCK_DETECT
};

EXTERN IDeviceDriver * CDECL CreateDebugInfo(uint_t uDeviceNo, void *pvParameter)
{
    g_pDebugInfo = new CDebugInfo;
    if (NULL == g_pDebugInfo) {
        kprintf("ERROR: Not enough memory to create CDebugInfo!\n");
        return NULL;
    }

    g_pDebugInfo->AddRef();

    return g_pDebugInfo;
}

ECode CDebugInfo::Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CDebugInfo::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CDebugInfo::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
#ifdef _arm
    if (nControlCode ==  0x10) {
        CProcess *pProcess = GetCurrentProcess();
        Thread *pThread = GetCurrentThread();
        InterruptContext *pContext = (InterruptContext *)pThread->m_vaKernelStack - 1;
        kprintf("$$$$$$$$$$$$Trace Current Thread Stack Start $$$$$$$$$$\n");
        TraceStack(pProcess, (Address)pContext->r11);
        kprintf("$$$$$$$$$$$$Trace Current Thread Stack End $$$$$$$$$$$$\n");
        return NOERROR;
    }
#endif
	kprintf("You Can watch the debuginfo detail!\n");
    Show();
    return NOERROR;
}

void CDebugInfo::Dispose()
{
    delete this;
}

void CDebugInfo::Show()
{
	CProcess *pProcess = NULL;
	Thread *pCurrentThread = NULL;
	ThreadId tid;
	UInt32 size;

#ifndef _neptune
	pProcess = GetCurrentProcess();
	pCurrentThread = GetCurrentThread();

	kprintf("\n\n\n");
	kprintf("***************************current information***********************\n");
	if (pProcess) {
	    kprintf("Process    Name :        %S \n", pProcess->m_wszName);
	    kprintf("Process         :        %x\n", pProcess);
	    if (pCurrentThread) {
			pCurrentThread->GetId(&tid);
			GetCurrentThreadPhyscialMemorySize(pCurrentThread, &size);

			kprintf("Thread     Name :        %S\n", pCurrentThread->m_wszName);
			kprintf("Thread          :        %x\n", pCurrentThread);
			kprintf("ThreadID   is   :        %x\n", tid);
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
			kprintf("Thread   USerID :        %x\n", GET_THREAD_USERID(pCurrentThread));
#endif //MULTI_RESOURCE_DEADLOCK_DETECT
			kprintf("max stack physical memory size: %d\n", size);
		} else {
			kprintf("No Current Thread!\n");
		}
    } else {
        kprintf("No Current Process!\n");
    }
#endif
    //printf thread info
    kprintf("\n\n\n");
	kprintf("************************SuperExe.exe information******************************\n");

	//search processs
	ForEachDLinkNode(CProcess *, pProcess, &CProcess::s_processList) {
		if (!wcscmp(pProcess->m_wszName, L"SuperExe.exe")) {
			pProcess->Activate();
			PrintModuleView(pProcess);
			PrintProcessContext(pProcess);
			kprintf("Thread Info End!\n");
			return;
		}
	}

    kprintf("\n");
	return;
}

void CDebugInfo::PrintThreadContext(CProcess *pProcess)
{
	InProcessLinkNode *pInProcessLinkNode;
	ForEachDLinkNode(InProcessLinkNode *, pInProcessLinkNode,
                    &pProcess->m_threadList) {
		Thread *pThread = THIS(pInProcessLinkNode, Thread, m_inProcLink);
		PrintThreadInfo(pProcess, pThread);
	}

	return;
}

void CDebugInfo::PrintProcessContext(CProcess *pProcess)
{
	if(pProcess == NULL) return;
	kprintf("\n\n\n");
	kprintf("----------------------------------------------------------------\n");
	kprintf("----------------------------------------------------------------\n");
	kprintf("The Process Name is %S \n", pProcess->m_wszName);
//	kprintf("The process ID for DEADLOCK_DETECT: %x\n", pProcess);

	PrintThreadContext(pProcess);
	return;
}

void CDebugInfo::PrintThreadInfo(CProcess *pProcess, Thread *pThread)
{
	Address addr;
	UInt32 size;
	ThreadId tid;
#ifdef _arm
	InterruptContext *pContext = (InterruptContext *)pThread->m_vaKernelStack - 1;
	addr = pContext->r11;
#else //_arm
	addr = 0;
#endif //_x86

	kprintf("\n\n\n");
	kprintf("#########################################################\n");
	pThread->GetId(&tid);
	GetCurrentThreadPhyscialMemorySize(pThread, &size);

	kprintf("Thread  Name        : %S\n", pThread->m_wszName);
	kprintf("Thread              : %x\n", pThread);
	kprintf("Thread->GetId       : %x\n", tid);
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
	kprintf("Thread  USerID      : %x\n", GET_THREAD_USERID(pThread));
#endif //MULTI_RESOURCE_DEADLOCK_DETECT
	kprintf("max stack physical memory size	: %d\n", size);

	switch (pThread->m_uState) {
		case ThreadState_Running:
			kprintf("Treade state 	: ThreadState_Running!\n");
			break;
		case ThreadState_Sleeping:
			kprintf("Treade state	: ThreadState_Sleeping!\n");
			break;
		case ThreadState_InterruptibleSleeping:
			kprintf("Treade state   : ThreadState_InterruptibleSleeping!\n");
			break;
		case ThreadState_Suspended:
			kprintf("Treade state	: ThreadState_Suspended!\n");
			break;
		case ThreadState_SleepingSuspended:
			kprintf("Treade state	: ThreadState_SleepingSuspended!\n");
			break;
		case ThreadState_Finished:
			kprintf("Treade state	: ThreadState_Finished!\n");
			break;
		case ThreadState_Unstarted:
			kprintf("Treade state   : ThreadState_Unstarted!\n");
			break;
		default:
			break;
	}

#ifdef _arm
	kprintf("r0  = %8x, r1  = %8x, r2  = %8x, r3  = %8x\n"
            "r4  = %8x, r5  = %8x, r6  = %8x, r7  = %8x\n"
            "r8  = %8x, r9  = %8x, r10 = %8x, r11 = %8x\n"
            "r12 = %8x, sp  = %8x, lr  = %8x, pc  = %8x\n"
            "cpsr= %8x\n",
            pContext->r0, pContext->r1, pContext->r2, pContext->r3,
            pContext->r4, pContext->r5, pContext->r6, pContext->r7,
            pContext->r8, pContext->r9, pContext->r10, pContext->r11,
            pContext->r12, pContext->sp, pContext->lr, pContext->pc,
            pContext->cpsr);
#endif //_arm

    kprintf("*********************************************************\n");
    TraceStack(pProcess, addr);
#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
    DeadLockDetection(pThread, pProcess);
#endif //MULTI_RESOURCE_DEADLOCK_DETECT
//    TraceMemory(pContext);
	return;
}

//
//    Stack Backtrace Structure
//    =========================
//
//    +---------------------+
//    |       save pc       |  [fp]       fp points here
//    +---------------------+
//    |  return link value  |  [fp, #-4]
//    +---------------------+
//    |   return sp value   |  [fp, #-8]
//    +---------------------+
//    |   return fp value   |  [fp, #-12] points to next structure

Boolean CDebugInfo::IsValidAddr(CProcess *pProcess, const UInt32 uAddr)
{
    DLinkNode *pCurrentList;
    VSegment *pSeg;

    if (uAddr < COMMON_LIMIT) {
        pCurrentList = &pProcess->m_AS.m_commList;
    } else {
        pCurrentList = &pProcess->m_AS.m_privList;
    }

    ForEachDLinkNode(VSegment *, pSeg, pCurrentList) {
        if (FALL_INTO(uAddr, pSeg->m_uBase, pSeg->m_uSize)) {
			return TRUE;
        }
    }

    if (FALL_INTO((UInt32)uAddr, (UInt32)PROXY_ENTRY_BASE, (UInt32)PAGE_SIZE)
        || FALL_INTO((UInt32)uAddr,
        (UInt32)USER_BLOCK_BASE, (UInt32)USER_BLOCK_SIZE)) {

            return TRUE;
    }

    return FALSE;
}

void CDebugInfo::TraceStack(CProcess *pProcess, Address addr)
{
	if (0 == addr) {
		kprintf("No Trace!\n");
		return;
	}
	int *fp = (int *)addr;
    kprintf("fp is %x\n", fp);
    if ((0x60000000 > (Int32)fp) || ((UInt32)fp > 0x7FC00000)) {
        kprintf(">>>>>>>>>>>>>>>>>> no stack <<<<<<<<<<<<<<<<\n");
	    return;
	}
	while (fp) {
		kprintf("pc[0x%x] ", *(fp));
		if (IsValidAddr(pProcess, *(fp - 1))) {
			GetModuleBase(pProcess, *(fp - 1));
			fp = (int *)(*(fp - 3));
		} else {
			kprintf("It is an invalid address!\n");
			return;
		}
	}

	return;
}

void CDebugInfo::TraceMemory(Address addr)
{
//	uint32_t sp;
//	uint32_t bottom;
//
//	sp = pContext->sp;
//	kprintf("the sp is %x \n", sp);
//
//	bottom = (sp & ~(USER_STACK_ALIGN - 1)) + USER_STACK_ALIGN;
//	kprintf("the top is %x \n", bottom);
//	int num = 0;
//
//	for ( ;sp < bottom; sp++) {
//		if (!GetModuleBase(*(uint32_t *)(sp))) continue;
//		num++;
//		if (num > 20) break;
//	}
	return;
}

ECode CDebugInfo::PrintModuleView(CProcess *pProcess)
{
	ModuleView *pModuleView;
	if (pProcess) {
        //all module name, baseAddress
        ForEachDLinkNode(ModuleView *, pModuleView, &pProcess->m_moduleList) {
            if (NULL == pModuleView->m_pModule) continue;
            kprintf("Module: %x %S \n",
                pModuleView->m_pModule->m_uImageBase,
                pModuleView->m_pModule->m_wszName
                );
        }
   	}

	return NOERROR;
}

ECode CDebugInfo::GetModuleBase(CProcess *pProcess, uint32_t retAddr)
{
	ModuleView *pModuleView;
	ForEachDLinkNode(ModuleView *, pModuleView, &pProcess->m_moduleList) {
		if (NULL == pModuleView->m_pModule) continue;
		if(FALL_INTO(retAddr, pModuleView->m_pModule->m_uImageBase,
                    pModuleView->m_pModule->m_uTotalSize)) {
        	kprintf("retAddr[0x%x] Module:[%S]0x%x [retAddr-base] %x \n", retAddr,
		        pModuleView->m_pModule->m_wszName,
		        pModuleView->m_pModule->m_uImageBase,
		        retAddr - pModuleView->m_pModule->m_uImageBase);
	        return NOERROR;
        }
    }

    kprintf("0x%x no suggest\n", retAddr);
    return NOERROR;
}

ECode CDebugInfo::GetCurrentThreadPhyscialMemorySize(Thread *pCurrentThread, UInt32 *pSize)
{
	VSegment * pSeg = NULL;
	ForEachDLinkNode(VSegment *, pSeg, &(pCurrentThread->m_pOwner->m_AS.m_privList)) {
		if (pCurrentThread->m_uUserStackBase == pSeg->m_uBase) {
			*pSize = pSeg->GetPhyscialMemorySize();
			return NOERROR;
		}
	}

	return E_DOES_NOT_EXIST;
}

#ifdef MULTI_RESOURCE_DEADLOCK_DETECT
void CDebugInfo::DeadLockDetection(Thread *pThread, CProcess *pProcess)
{
    if (pThread->m_pWaitingSyncObject == NULL) {
        return;
    }

    int nDeadLockSection[256];
    int nSyncObjFlag;

    nDeadLockSection[1] = (int)pThread;

    int *pSyncObj = (int *)pThread->m_pWaitingSyncObject;
    int *pThd = (int *)pThread->m_pWaitingSyncObject->GetOwner();
    int *pTmpThd = NULL;

    if (pThd == NULL) {
        if (IsCSNodeEvent((SyncObject *)(pThread->m_pWaitingSyncObject), pProcess)) {
            pThd = GetSynObjOwner((SyncObject *)(pThread->m_pWaitingSyncObject), pProcess);
            if (pThd == NULL) return;
            nSyncObjFlag = CRITICALSECTION_FLAG;
            kprintf("The Thread Wait One CRITICALSECTION , Owner is %8x\n", pThd);
        } else {
            kprintf("The Thread Wait One Event!\n");
            return;
        }
    } else {
        kprintf("The Thread Wait One Mutex, Owner is %8x\n", pThd);
        nSyncObjFlag = MUTEX_FLAG;
    }

    nDeadLockSection[2] = nSyncObjFlag;
    nDeadLockSection[3] = (int)pThread->m_pWaitingSyncObject;

    if ((int)pThd == nDeadLockSection[1]) return;

    UInt32 uIndex = 4;
    do {
        nDeadLockSection[uIndex++] = (int)pThd;
        for (int i = uIndex - 4; i >= 1; i -= 3) {
            if ((int)pThd == nDeadLockSection[i]) {
                nDeadLockSection[0] = uIndex;
                kprintf("\n****************** DEAD LOCK ******************\n");
                OutPutDeadLockInfo(nDeadLockSection, i);
                kprintf("\n***********************************************\n");
                return;
            }
        }

        pSyncObj = GetRequestSynObj((Thread *)pThd);
        if (NULL == pSyncObj) return;

        pTmpThd = (int *)((SyncObject *)pSyncObj)->GetOwner();
        if (NULL == pTmpThd) {
            if (IsCSNodeEvent((SyncObject *)pSyncObj, pProcess)) {
                pTmpThd = GetSynObjOwner((SyncObject *)pSyncObj, pProcess);
                if (NULL == pTmpThd) return;
                nSyncObjFlag = CRITICALSECTION_FLAG;
            } else {
                return;
            }
        } else {
            nSyncObjFlag = MUTEX_FLAG;
        }

        nDeadLockSection[uIndex++] = nSyncObjFlag;
        nDeadLockSection[uIndex++] = (int)pSyncObj;

        assert(pThd != pTmpThd);
        assert(uIndex < 256);

        pThd = pTmpThd;
        if (pThd == NULL) return;
    }while(TRUE);

    return;
}

void CDebugInfo::OutPutDeadLockInfo(int *pDeadLockInfo, int nDeadLockPos)
{
    kprintf("\n");
    Thread *pThread = NULL;

    if (1 != nDeadLockPos) {
        pThread = (Thread *)pDeadLockInfo[1];
        kprintf("Thread(ID):%x", GET_THREAD_USERID(pThread));
    }

    for (int i = 3; i < nDeadLockPos; i++) {
        if ((i % 3 == 0)) {
            if (pDeadLockInfo[i - 1] == CRITICALSECTION_FLAG) {
                kprintf(" --->CS :%x ====> ", pDeadLockInfo[i]);
            } else {
                kprintf(" --->Mutex :%x ====> ", pDeadLockInfo[i]);
            }
        } else {
            if ((i % 3 == 1)) {
                pThread = (Thread *)pDeadLockInfo[i];
                kprintf("Thread(ID):%x", GET_THREAD_USERID(pThread));
            }
        }
    }
    kprintf(" $#DeadLock#${ ");
    for (int i = nDeadLockPos; i < pDeadLockInfo[0] - 1; i++) {
        if ((i % 3 == 0)) {
            if (pDeadLockInfo[i - 1] == CRITICALSECTION_FLAG) {
                kprintf(" --->CS :%x ====> ", pDeadLockInfo[i]);
            } else {
                kprintf(" --->Mutex :%x ====> ", pDeadLockInfo[i]);
            }
        } else {
            if ((i % 3 == 1)) {
                pThread = (Thread *)pDeadLockInfo[i];
                kprintf("Thread(ID):%x", GET_THREAD_USERID(pThread));
            }
        }
    }

    pThread = (Thread *)pDeadLockInfo[nDeadLockPos];
    kprintf("Thread(ID):%x }$#DeadLock#$\n", GET_THREAD_USERID(pThread));
    return;
}

int* CDebugInfo::GetRequestSynObj(Thread *pThread)
{
    return (int *)(pThread->m_pWaitingSyncObject);
}

int *CDebugInfo::GetSynObjOwner(SyncObject *pSyncObj, CProcess *pProcess)
{
    int idx=0;
    int nUserThreadId = 0;
    OwnerNode *pNode;
    DLinkNode *pNodeList = (DLinkNode *)pProcess->m_nCSOwnerInfo;

    for (idx = 0; idx < THREAD_MINIMUM_AVAILABLE; idx++) {
        ForEachDLinkNode(OwnerNode *, pNode, &pNodeList[idx])
        {
            if ((int)&((EventNode *)(pNode->event))->m_event == (int) pSyncObj) {
                nUserThreadId = pNode->nThreadId;
                assert(nUserThreadId != 0);
                break;
            }
        }
    }

    if (nUserThreadId == 0) {
        return NULL;
    }

    InProcessLinkNode *pInProcessLinkNode;
    ForEachDLinkNode(InProcessLinkNode *, pInProcessLinkNode, \
        &pProcess->m_threadList) {
            Thread *pThread = THIS(pInProcessLinkNode, Thread, m_inProcLink);
            if (pThread->m_uUserStackBase) {
                if ((int)nUserThreadId == \
                    (int)((pThread->m_uUserStackBase & ~(USER_STACK_ALIGN - 1)) + 1)) {
                    return (int *)pThread;
                }
            }
    }

    return NULL;
}

Boolean CDebugInfo::IsCSNodeEvent(SyncObject *pSyncObj, CProcess *pProcess)
{
    register EventNode *pTmpNode;
    ForEachDLinkNode(EventNode *, pTmpNode, &(pProcess->m_eventList)) {
        if ((int *)&(pTmpNode->m_event) == (int *)pSyncObj) {
            return TRUE;
        }
    }
    return FALSE;
}

#endif //MULTI_RESOURCE_DEADLOCK_DETECT
